/*
* Created by boil on 2023/10/26.
*/

#ifndef RENDU_RESOURCE_POOL_H
#define RENDU_RESOURCE_POOL_H

#include "define.h"
#include <memory>
#include <functional>

RD_NAMESPACE_BEGIN

  template<typename C>
  class ResourcePool_l;

  template<typename C>
  class ResourcePool;

  template<typename C>
  class shared_ptr_imp : public std::shared_ptr<C> {
  public:
    shared_ptr_imp() {}

    /**
     * 构造智能指针
     * @param ptr 裸指针
     * @param weakPool 管理本指针的循环池
     * @param quit 对接是否放弃循环使用
     */
    shared_ptr_imp(
      C *ptr, const std::weak_ptr<ResourcePool_l<C>> &weakPool, std::shared_ptr<std::atomic_bool> quit,
      const std::function<void(C *)> &on_recycle);

    /**
     * 放弃或恢复回到循环池继续使用
     * @param flag
     */
    void quit(bool flag = true) {
      if (_quit) {
        *_quit = flag;
      }
    }

  private:
    std::shared_ptr<std::atomic_bool> _quit;
  };

  template<typename C>
  class ResourcePool_l : public std::enable_shared_from_this<ResourcePool_l<C>> {
  public:
    using ValuePtr = shared_ptr_imp<C>;

    friend class shared_ptr_imp<C>;

    friend class ResourcePool<C>;

    ResourcePool_l() {
      _alloc = []() -> C * { return new C(); };
    }

#if defined(SUPPORT_DYNAMIC_TEMPLATE)
    template <typename... ArgTypes>
      ResourcePool_l(ArgTypes &&...args) {
          _alloc = [args...]() -> C * { return new C(args...); };
      }
#endif // defined(SUPPORT_DYNAMIC_TEMPLATE)

    ~ResourcePool_l() {
      for (auto ptr: _objs) {
        delete ptr;
      }
    }

    void setSize(size_t size) {
      _pool_size = size;
      _objs.reserve(size);
    }

    ValuePtr obtain(const std::function<void(C *)> &on_recycle = nullptr) {
      return ValuePtr(getPtr(), _weak_self, std::make_shared<std::atomic_bool>(false), on_recycle);
    }

    std::shared_ptr<C> obtain2() {
      auto weak_self = _weak_self;
      return std::shared_ptr<C>(getPtr(), [weak_self](C *ptr) {
        auto strongPool = weak_self.lock();
        if (strongPool) {
          //放入循环池
          strongPool->recycle(ptr);
        } else {
          delete ptr;
        }
      });
    }

  private:
    void recycle(C *obj) {
      auto is_busy = _busy.test_and_set();
      if (!is_busy) {
        //获取到锁
        if (_objs.size() >= _pool_size) {
          delete obj;
        } else {
          _objs.emplace_back(obj);
        }
        _busy.clear();
      } else {
        //未获取到锁
        delete obj;
      }
    }

    C *getPtr() {
      C *ptr;
      auto is_busy = _busy.test_and_set();
      if (!is_busy) {
        //获取到锁
        if (_objs.size() == 0) {
          ptr = _alloc();
        } else {
          ptr = _objs.back();
          _objs.pop_back();
        }
        _busy.clear();
      } else {
        //未获取到锁
        ptr = _alloc();
      }
      return ptr;
    }

    void setup() { _weak_self = this->shared_from_this(); }

  private:
    size_t _pool_size = 8;
    std::vector<C *> _objs;
    std::function<C *(void)> _alloc;
    std::atomic_flag _busy{false};
    std::weak_ptr<ResourcePool_l> _weak_self;
  };

/**
 * 循环池，注意，循环池里面的对象不能继承enable_shared_from_this！
 * @tparam C
 */
  template<typename C>
  class ResourcePool {
  public:
    using ValuePtr = shared_ptr_imp<C>;

    ResourcePool() {
      pool.reset(new ResourcePool_l<C>());
      pool->setup();
    }

#if defined(SUPPORT_DYNAMIC_TEMPLATE)
    template <typename... ArgTypes>
      ResourcePool(ArgTypes &&...args) {
          pool = std::make_shared<ResourcePool_l<C>>(std::forward<ArgTypes>(args)...);
          pool->setup();
      }
#endif // defined(SUPPORT_DYNAMIC_TEMPLATE)

    void setSize(size_t size) { pool->setSize(size); }

    //获取一个对象，性能差些，但是功能丰富些
    ValuePtr obtain(const std::function<void(C *)> &on_recycle = nullptr) { return pool->obtain(on_recycle); }

    //获取一个对象，性能好些
    std::shared_ptr<C> obtain2() { return pool->obtain2(); }

  private:
    std::shared_ptr<ResourcePool_l<C>> pool;
  };

  template<typename C>
  shared_ptr_imp<C>::shared_ptr_imp(C *ptr,
                                    const std::weak_ptr<ResourcePool_l<C> > &weakPool,
                                    std::shared_ptr<std::atomic_bool> quit,
                                    const std::function<void(C *)> &on_recycle) :
    std::shared_ptr<C>(ptr, [weakPool, quit, on_recycle](C *ptr) {
      if (on_recycle) {
        on_recycle(ptr);
      }
      auto strongPool = weakPool.lock();
      if (strongPool && !(*quit)) {
        //循环池还在并且不放弃放入循环池
        strongPool->recycle(ptr);
      } else {
        delete ptr;
      }
    }), _quit(std::move(quit)) {}

RD_NAMESPACE_END

#endif //RENDU_RESOURCE_POOL_H
